package plsqlfs;

import java.util.HashSet;
import java.util.Set;

public abstract class Interpolator {

	protected abstract String value(String key);

	public String interpolateFully(String text) {
		Set<String> seen = new HashSet<String>();
		
		String interpolation = interpolate(text);
		
		String interpolationBefore, interpolationAfter;
		do {
			if ( !seen.add( interpolation ) ) {
				throw new IllegalStateException();
			}
			
			interpolationBefore = interpolation;
			
			interpolation = interpolate( interpolation );
			
			interpolationAfter = interpolation;
		} while ( !interpolationBefore.equals(interpolationAfter) );
		
		return interpolation;
	}

	public String interpolate(String text) {
		if ( text == null ) {
			throw new NullPointerException();
		}
		
		StringBuffer buffer = new StringBuffer();
		
		int index = 0;
		int length = text.length();
		
		while ( index < length ) {
			char ch = text.charAt( index );
			
			switch ( ch ) {
			case '$':
				// UNIX-style variable
				if ( (index + 1) < length && text.charAt( index + 1 ) == '{' ) {
					int indexOfClosingBracket = text.indexOf( '}', index + 1 );
					if ( indexOfClosingBracket == -1 ) {
						// False alarm, this variable doesn't end
						buffer.append( ch );
						index++;
					} else {
						String key = text.substring( index + 2, indexOfClosingBracket );
						String value = value(key);
						if ( value == null ) {
							buffer.append( ch );
							index++;
						} else {
							buffer.append( value );
							index = indexOfClosingBracket + 1;
						}
					}
				} else {
					// TODO move along until first non-word character is encountered
					int toIndex = index + 1;
					while ( toIndex < length && isKeyChar( text.charAt(toIndex) ) ) toIndex++;
					
					String key = text.substring( index + 1, toIndex );
					String value = value(key);
					if ( value == null ) {
						buffer.append( ch );
						index++;
					} else {
						buffer.append( value );
						index = toIndex;
					}
				}
				break;
				
			case '%':
				// Windows-style variable
				int indexOfClosingPercentSign = text.indexOf( '%', index + 1 );
				if ( indexOfClosingPercentSign == -1 ) {
					// False alarm, this variable doesn't end
					buffer.append( ch );
					index++;
				} else {
					String key = text.substring( index + 1, indexOfClosingPercentSign );
					String value = value(key);
					if ( value == null ) {
						buffer.append( ch );
						index++;
					} else {
						buffer.append( value );
						index = indexOfClosingPercentSign + 1;
					}
				}
				break;
				
			default:
				buffer.append( ch );
				index++;
				break;
			}
		}
		
		return buffer.toString();
	}

	private boolean isKeyChar(char ch) {
		return ( 'A' <= ch && ch <= 'Z' )
		    || ( 'a' <= ch && ch <= 'z' )
		    || ( '0' <= ch && ch <= '9' )
		    || ( ch == '_' );
	}

}
